    // Copyright (C) 2003  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_DIR_NAV_KERNEL_2_CPp_
    #define DLIB_DIR_NAV_KERNEL_2_CPp_

    #include "../platform.h"

    #ifdef DLIB_POSIX

    #include "dir_nav_kernel_2.h"

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // file object implementation
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        void file::
        init (
            const std::string& name
        )
        {
            using namespace std;

            char buf[PATH_MAX];
            if (realpath(name.c_str(),buf) == 0)
            {
                // the file was not found
                throw file_not_found("Unable to find file " + name);
            }
            state.full_name = buf;

            string::size_type pos = state.full_name.find_last_of(directory::get_separator());
            if (pos == string::npos)
            {
                // no valid full path has no separtor characters.
                throw file_not_found("Unable to find file " + name);
            }
            state.name = state.full_name.substr(pos+1);

            // now find the size of this file
            struct stat64 buffer;
            if (::stat64(state.full_name.c_str(), &buffer) ||
                S_ISDIR(buffer.st_mode))
            {
                // there was an error during the call to stat64 or
                // name is actually a directory
                throw file_not_found("Unable to find file " + name);
            }
            else
            {
                state.file_size = static_cast<uint64>(buffer.st_size);

                state.last_modified = std::chrono::system_clock::from_time_t(buffer.st_mtime);
    #ifdef _BSD_SOURCE
                state.last_modified += std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::nanoseconds(buffer.st_atim.tv_nsec));
    #endif
            }

        }

    // ----------------------------------------------------------------------------------------

        bool file::
        operator == (
            const file& rhs
        ) const
        {
            using namespace std;
            if (state.full_name.size() == 0 && rhs.state.full_name.size() == 0)
                return true;

            // These files might have different names but actually represent the same
            // file due to the presence of symbolic links.
            char buf[PATH_MAX];
            string left, right;
            if (realpath(state.full_name.c_str(),buf) == 0)
                return false;
            left = buf;

            if (realpath(rhs.state.full_name.c_str(),buf) == 0)
                return false;
            right = buf;

            return (left == right);
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // directory object implementation
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        void directory::
        init (
            const std::string& name
        )
        {
            using namespace std;

            char buf[PATH_MAX];
            if (realpath(name.c_str(),buf) == 0)
            {
                // the directory was not found
                throw dir_not_found("Unable to find directory " + name);
            }
            state.full_name = buf;

            const char sep = get_separator();
            if (is_root_path(state.full_name) == false)
            {
                // ensure that thre is not a trialing separator
                if (state.full_name[state.full_name.size()-1] == sep)
                    state.full_name.erase(state.full_name.size()-1);

                // pick out the directory name
                string::size_type pos = state.full_name.find_last_of(sep);
                state.name = state.full_name.substr(pos+1);
            }
            else
            {
                // ensure that there is a trailing separator
                if (state.full_name[state.full_name.size()-1] != sep)
                    state.full_name += sep;
            }

            struct stat64 buffer;
            // now check that this is actually a valid directory
            if (::stat64(state.full_name.c_str(),&buffer))
            {
                // the directory was not found
                throw dir_not_found("Unable to find directory " + name);
            }
            else if (S_ISDIR(buffer.st_mode) == 0)
            {
                // It is not a directory
                throw dir_not_found("Unable to find directory " + name);
            }
        }

    // ----------------------------------------------------------------------------------------

        char directory::
        get_separator (
        )
        {
            return '/';
        }

    // ----------------------------------------------------------------------------------------

        bool directory::
        operator == (
            const directory& rhs
        ) const
        {
            using namespace std;
            if (state.full_name.size() == 0 && rhs.state.full_name.size() == 0)
                return true;

            // These directories might have different names but actually represent the same
            // directory due to the presence of symbolic links.
            char buf[PATH_MAX];
            string left, right;
            if (realpath(state.full_name.c_str(),buf) == 0)
                return false;
            left = buf;

            if (realpath(rhs.state.full_name.c_str(),buf) == 0)
                return false;
            right = buf;

            return (left == right);
        }

    // ----------------------------------------------------------------------------------------

        const directory directory::
        get_parent (
        ) const
        {
            using namespace std;
            // if *this is the root then just return *this
            if (is_root())
            {
                return *this;
            }
            else
            {
                directory temp;

                const char sep = get_separator();

                string::size_type pos = state.full_name.find_last_of(sep);
                temp.state.full_name = state.full_name.substr(0,pos);

                if ( is_root_path(temp.state.full_name))
                {
                    temp.state.full_name += sep;
                }
                else
                {
                    pos = temp.state.full_name.find_last_of(sep);
                    if (pos != string::npos)
                    {
                        temp.state.name = temp.state.full_name.substr(pos+1);
                    }
                    else
                    {
                        temp.state.full_name += sep;
                    }
                }
                return temp;
            }
        }

    // ----------------------------------------------------------------------------------------

        bool directory::
        is_root_path (
            const std::string& path
        ) const
        {
            const char sep = get_separator();
            if (path.size() == 1 && path[0] == sep)
                return true;
            else
                return false;
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_POSIX

    #endif // DLIB_DIR_NAV_KERNEL_2_CPp_